#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include "protocol.h"
#include "Video.h"
#include "Color2Grey.h"
#include "CaptureIsp.h"
#include "Qrcode.h"
#include "DevConfig.h"
#include "System.h"

#define TAG	"IBT_VIDEO"

/**************************************************************************************/
#define MAX_VIDEO_BUFFER            (2)
#define MAX_VIDEO_BUFFER_SIZE       (500*1024)

/**************************************************************************************/
typedef struct {
    AV_U8       buffMax;
    AV_U32      buffSize;
    MEDIA_PACK_VIDEO stPackVideo[MAX_VIDEO_BUFFER];
}VIDEO_BUFF_INFO_S;

/**************************************************************************************/
static VIDEO_REG_ATTR_S gs_stVideoRegAttr[VIDEO_VENC_CHN_MAX_NUM] = {0};
static VIDEO_BUFF_INFO_S gs_stVideoBuffInfo;


/**************************************************************************************/

static int OnVideoMainStreamData(int chn, void *data)
{
    int ret = 0;
    static int cnt = 0;
    struct timespec ts;

    if (cnt == gs_stVideoBuffInfo.buffMax) {
        cnt=0;
    }

#ifdef __HuaweiLite__
    ts.tv_sec = 2; 
    ts.tv_nsec = 0;
#else
    if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
        LOGE("clock_gettime CLOCK_REALTIME failed:%s\n", strerror(errno));
        return 0;
    }
    ts.tv_sec += 2;
#endif /* __HuaweiLite__ */  
    ret = sem_timedwait(&gs_stVideoBuffInfo.stPackVideo[cnt].writeSem, &ts);
    if (ret < 0) {
        if (errno == ETIMEDOUT) {
            LOGE("sem_timedwait() timed out\n");
            return -1;
        } else {
            LOGE("sem_timedwait() error\n");
            return -1;
        }
    }

    ret = VideoGetOpsHandle()->video_copy_data(chn, data, &gs_stVideoBuffInfo.stPackVideo[cnt], gs_stVideoBuffInfo.buffSize);
    if (ret < 0) {
        LOGE("copy to buf failed\n");
        return -1;
    }

    sem_post(&gs_stVideoBuffInfo.stPackVideo[cnt].readSem);
    cnt++;
    
    return 0;
}

static int OnVideoSecondStreamData(int chn, void *data)
{
    return 0;
}

void *tskVideoMainStreamSend(void *args)
{
    int chn = 0;
    int index = 0;
    int bufCnt = 0;
    
    SET_THREAD_NAME("tskVideoMainStreamSend");
    pthread_detach(pthread_self());
    
    while (1)
    {
        if (bufCnt == (gs_stVideoBuffInfo.buffMax)) {
            bufCnt = 0;
        }
        
        sem_wait(&gs_stVideoBuffInfo.stPackVideo[bufCnt].readSem);
        
        if(gs_stVideoBuffInfo.stPackVideo[bufCnt].frameSize == 0) {
            sem_post(&gs_stVideoBuffInfo.stPackVideo[bufCnt].writeSem);
            continue;
        }
        
        index = 0;	
        AV_MUTEX_LOCK(gs_stVideoRegAttr[chn].mutexLock);
        for (index = 0; index < VIDEO_REG_MAX_NUM; index++)
        {
            if (NULL != gs_stVideoRegAttr[chn].pfnRegVideoCbArry[index])
            {
                gs_stVideoRegAttr[chn].pfnRegVideoCbArry[index](chn, gs_stVideoBuffInfo.stPackVideo[bufCnt]);
            }
        }
        AV_MUTEX_UNLOCK(gs_stVideoRegAttr[chn].mutexLock);

        sem_post(&gs_stVideoBuffInfo.stPackVideo[bufCnt].writeSem);
        bufCnt++;
    }

    pthread_exit(0);
}

void *tskVideoSecondStreamSend(void *args)
{
    SET_THREAD_NAME("tskVideoSecondStreamSend");
    pthread_detach(pthread_self());

    pthread_exit(0);
}

static int video_register_attr_init(void)
{
    static char bInit = 0;

    if (!bInit) 
    {
	    int i = 0;
        for (i = 0; i < VIDEO_VENC_CHN_MAX_NUM; i++) {
            AV_MUTEX_INIT_LOCK(gs_stVideoRegAttr[i].mutexLock);
            memset(gs_stVideoRegAttr[i].pfnRegVideoCbArry, NULL, sizeof(gs_stVideoRegAttr[i].pfnRegVideoCbArry));
        }
        bInit = 1;
    }

    return 0;
}

int VideoRegisterRecvData(int chn, VideoRecvCallback pfnRcvVideoCb)
{
    int index = 0;

    video_register_attr_init();

    if (chn < 0 || chn >= VIDEO_VENC_CHN_MAX_NUM) {
        printf("chn (%d) is invalid!\n");
        return -1;
    }

    for (index = 0; index < VIDEO_REG_MAX_NUM; index++)
    {
        if (NULL == gs_stVideoRegAttr[chn].pfnRegVideoCbArry[index]) {
            break;
        } else if (pfnRcvVideoCb == gs_stVideoRegAttr[chn].pfnRegVideoCbArry[index]) {
            printf("This function has been registered !\n");
            return -1;
        }
    }

    AV_MUTEX_LOCK(gs_stVideoRegAttr[chn].mutexLock);

    if (index >= 0 && index < VIDEO_REG_MAX_NUM)
    {
        gs_stVideoRegAttr[chn].pfnRegVideoCbArry[index] = pfnRcvVideoCb;
    }

    AV_MUTEX_UNLOCK(gs_stVideoRegAttr[chn].mutexLock);

    return 0;
}

int VideoUnRegisterRecvData(int chn, VideoRecvCallback pfnRcvVideoCb)
{
    int index = 0;

    AV_MUTEX_LOCK(gs_stVideoRegAttr[chn].mutexLock);

    for (index = 0; index < VIDEO_REG_MAX_NUM; index++)
    {
        if (pfnRcvVideoCb == gs_stVideoRegAttr[chn].pfnRegVideoCbArry[index])
        {
            gs_stVideoRegAttr[chn].pfnRegVideoCbArry[index] = NULL;
            break;
        }	
    }
    AV_MUTEX_UNLOCK(gs_stVideoRegAttr[chn].mutexLock);

    return 0;
}

extern VIDEO_OPS_FUNC_S *hisi_liteos_video_ops_handle(void);

VIDEO_OPS_FUNC_S *VideoGetOpsHandle(void)
{
    return hisi_liteos_video_ops_handle();
}

int VideoInit(void)
{
#ifdef MINI_APP
    return 0;
#endif /* MINI_APP */

    int i = 0;
    int ret = 0;
    void * videoBuf = NULL;
    int abuffSize  = 0;
    int offset = 0;
    int retErrNum = 0;
    pthread_t videoMainStreamPid;
    
    video_register_attr_init();

    /* video buffer malloc */
    memset(&gs_stVideoBuffInfo, 0, sizeof(gs_stVideoBuffInfo));
    gs_stVideoBuffInfo.buffMax = MAX_VIDEO_BUFFER;
    gs_stVideoBuffInfo.buffSize = MAX_VIDEO_BUFFER_SIZE;   
    abuffSize = gs_stVideoBuffInfo.buffMax * gs_stVideoBuffInfo.buffSize;
    videoBuf = (void *)malloc(abuffSize);
	if(videoBuf == NULL) {
		LOGE("malloc video info error\n");
		goto err_out0;
	}
	
    memset(videoBuf, 0, abuffSize);
    for (i = 0; i < gs_stVideoBuffInfo.buffMax; i++) {
        gs_stVideoBuffInfo.stPackVideo[i].pFrameData = ((AV_VOID *)videoBuf + offset);
        offset += gs_stVideoBuffInfo.buffSize;
        retErrNum = sem_init(&gs_stVideoBuffInfo.stPackVideo[i].writeSem, 0, 1);

        if (retErrNum < 0) {
            LOGE("sem_init readSem failed:%s\n", strerror(errno));
            goto err_out1;
        }

        retErrNum = sem_init(&gs_stVideoBuffInfo.stPackVideo[i].readSem, 0, 0);
        if (retErrNum < 0) {
            LOGE("sem_init readSem failed:%s\n", strerror(errno));
            goto err_out1;
        }
    }

    /* video init */
    ret = VideoGetOpsHandle()->init();
    if (ret < 0) {
        LOGE("video init failed\n");
        goto err_out1;
    }
    
    /* video main stream task run */
    ret = pthread_create(&videoMainStreamPid, NULL, &tskVideoMainStreamSend, NULL);
    if (ret != 0) {
        LOGE("pthread_create tskVideoMainStreamSend failed!\n");
        goto err_out1;
    }

    /* video venc start */
    ret = VideoGetOpsHandle()->video_venc_start(0, OnVideoMainStreamData);
    if (ret < 0) {
        LOGE("video_venc_start failed\n");
        goto err_out1;
    }

    return 0;
    
err_out1:
    free(videoBuf);
err_out0:
    return -1;
}

int VideoDeinit(void)
{
#ifdef MINI_APP
    return 0;
#endif /* MINI_APP */

    int i = 0;
    int ret = 0;

    /* video venc stop */
    ret = VideoGetOpsHandle()->video_venc_stop(0, OnVideoMainStreamData);
    AV_RETURN_WITH_INFO_IF_EXPR_FALSE(ret == 0, -1, "video_venc_stop");

    /* video deinit */
    ret = VideoGetOpsHandle()->deinit();
    AV_RETURN_WITH_INFO_IF_EXPR_FALSE(ret == 0, -1, "video deinit");

    /* video buffer free */
    for (i = 0; i < gs_stVideoBuffInfo.buffMax; i++)
    {
        if (NULL != gs_stVideoBuffInfo.stPackVideo[i].pFrameData) {
            free(gs_stVideoBuffInfo.stPackVideo[i].pFrameData);
            gs_stVideoBuffInfo.stPackVideo[i].pFrameData = NULL;
        }
    }

    return 0;
}

